﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;

using Konst3d.Efficiency.IO;

// label file:
// <sign:uint><uint:version><entry:[text***]><entry:[text***]>...

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class LabelsTextFile : BinaryFile
    {
        #region Types

        public struct SlotInfo
        {
            public long Offset;
            public int Capacity;
        }

        #endregion // Types

        #region Constants

        private const int MIN_CELL_SIZE = 64;
        private const uint SIGNATURE = 0xABABABAB;
        private const uint VERSION = 1;

        private readonly static byte[] _buff = new byte[MIN_CELL_SIZE];

        #endregion // Constants

        #region Create/Load Methods

        public static LabelsTextFile Create(string path, bool overwrite, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(cryptographicEngine != null);

            if (!overwrite && File.Exists(path))
            {
                throw new IOException(String.Format("The file is already exists {0}", path));
            }

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, cryptographicEngine.Encoding);
                writer = new BinaryWriter(stream, cryptographicEngine.Encoding);

                writer.Write(SIGNATURE);
                writer.Write(VERSION);

                writer.Flush();
                writer.Seek(0, SeekOrigin.Begin);

                return new LabelsTextFile(stream, reader, writer, cryptographicEngine);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        public static LabelsTextFile Open(string path, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(cryptographicEngine != null);

            if (!File.Exists(path))
            {
                throw new IOException(String.Format("Failed to locate labels file {0}.", path));
            }

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, cryptographicEngine.Encoding);
                writer = new BinaryWriter(stream, cryptographicEngine.Encoding);

                return new LabelsTextFile(stream, reader, writer, cryptographicEngine);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        #endregion // Create/Load Methods

        #region Constructor

        private LabelsTextFile(FileStream stream, BinaryReader reader, BinaryWriter writer, ICryptographicEngine cryptographicEngine) :
            base(stream, reader, writer, SIGNATURE, VERSION)
        {
            Contract.Requires(cryptographicEngine != null);

            _cryptographicEngine = cryptographicEngine;
        }

        #endregion // Constructor

        #region Public Methods

        public string GetLabel(long offset, int length)
        {
            return _cryptographicEngine.Decode(_reader.ReadBytes(length, offset)); // Encoding.UTF8.GetString(_reader.ReadBytes(length, offset), 0, length);
        }

        public void SetLabel(long offset, string label)
        {
            _writer.WriteBytes(_cryptographicEngine.Encode(label), (int)offset); // _writer.WriteBytes(Encoding.UTF8.GetBytes(label), (int)offset);
        }

        public SlotInfo AddLabel(string label)
        {
            var slot = new SlotInfo();
            var buff = _cryptographicEngine.Encode(label); // Encoding.UTF8.GetBytes(label);

            slot.Offset = _writer.Seek(0, SeekOrigin.End);

            if (buff.Length >= MIN_CELL_SIZE)
            {
                _writer.WriteBytes(buff);

                slot.Capacity = buff.Length;
            }
            else
            {
                _writer.WriteBytes(0, MIN_CELL_SIZE);
                _writer.WriteBytes(buff, (int)slot.Offset);

                slot.Capacity = MIN_CELL_SIZE;
            }

            return slot;
        }

        #endregion // Public Methods

        #region Fields

        private ICryptographicEngine _cryptographicEngine;

        #endregion // Fields
    }
}
